//
//  JKRCameraView.m
//  JKRCameraDemo
//
//  Created by tronsis_ios on 16/8/30.
//  Copyright © 2016年 tronsis_ios. All rights reserved.
//

#import "JKRCameraView.h"
#import "JKRCameraBackgroundView.h"
#import <AssetsLibrary/ALAssetsLibrary.h>
#import "NSString+FileSize.h"

#define kMoviePath @"kMoviePath"
#define kMovieTime @"kMovieTime"
#define kCachePath NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).lastObject
#define kVideoPath NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).lastObject

@interface JKRCameraView ()<JKRCameraBackgroundViewDelegate, JKRCameraBackgroundViewDatasource>

@property (nonatomic, strong) GPUImageVideoCamera *videoCamera;
@property (nonatomic, strong) GPUImageSaturationFilter *saturationFilter;
@property (nonatomic, strong) GPUImageView *filteredVideoView;
@property (nonatomic, strong) JKRCameraBackgroundView *backgroundView;
@property (nonatomic, strong) NSString *currentMoviePath;
@property (nonatomic, strong) NSMutableArray<NSDictionary *> *moviePaths;
@property (nonatomic, strong) GPUImageMovieWriter *movieWriter;

@end

@implementation JKRCameraView

- (instancetype)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    
    self.videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPresetiFrame1280x720 cameraPosition:AVCaptureDevicePositionBack];
    self.videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
    [self.videoCamera addAudioInputsAndOutputs];
    self.saturationFilter = [[GPUImageSaturationFilter alloc] init];
    self.filteredVideoView = [[GPUImageView alloc] initWithFrame:frame];
    [self.videoCamera addTarget:self.saturationFilter];
    [self.saturationFilter addTarget:self.filteredVideoView];
    [self.videoCamera startCameraCapture];
//    self.videoCamera.runBenchmark = YES;
    [self addSubview:self.filteredVideoView];
    
    _backgroundView = [[JKRCameraBackgroundView alloc] initWithFrame:frame];
    _backgroundView.delegate = self;
    _backgroundView.datasource = self;
    [self addSubview:_backgroundView];
    
    _moviePaths = [NSMutableArray array];
    
    [self getCacheCompleted:^(NSString *string) {
        NSLog(@"Cache: %@", string);
        [self clearCache];
        [self getCacheCompleted:^(NSString *string) {
            NSLog(@"Cache: %@", string);
        }];
    }];
    
    return self;
}

#pragma mark - 切换到后摄像头
- (void)cameraBackgroundDidClickChangeBack {
    [_saturationFilter removeAllTargets];
    _videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPresetiFrame1280x720 cameraPosition:AVCaptureDevicePositionBack];
    _videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
    [_videoCamera addAudioInputsAndOutputs];
    [_videoCamera addTarget:_saturationFilter];
    [_saturationFilter addTarget:_filteredVideoView];
    [_videoCamera startCameraCapture];
}

#pragma mark - 切换到前摄像头
- (void)cameraBackgroundDidClickChangeFront {
    [_saturationFilter removeAllTargets];
    _videoCamera = [[GPUImageVideoCamera alloc] initWithSessionPreset:AVCaptureSessionPresetiFrame1280x720 cameraPosition:AVCaptureDevicePositionFront];
    _videoCamera.outputImageOrientation = UIInterfaceOrientationPortrait;
    [_videoCamera addAudioInputsAndOutputs];
    [_videoCamera addTarget:_saturationFilter];
    [_saturationFilter addTarget:_filteredVideoView];
    [_videoCamera startCameraCapture];
}

#pragma mark - 打开闪光灯
- (void)cameraBackgroundDidClickOpenFlash {
    if (![_videoCamera.inputCamera isTorchModeSupported:AVCaptureTorchModeOn]) return;
    if (![_videoCamera.inputCamera lockForConfiguration:nil]) return;
    [_videoCamera.inputCamera setTorchMode:AVCaptureTorchModeOn];
    [_videoCamera.inputCamera unlockForConfiguration];
}

#pragma mark - 关闭闪光灯
- (void)cameraBackgroundDidClickCloseFlash {
    if (![_videoCamera.inputCamera isTorchModeSupported:AVCaptureTorchModeOn]) return;
    if (![_videoCamera.inputCamera lockForConfiguration:nil]) return;
    [_videoCamera.inputCamera setTorchMode:AVCaptureTorchModeOff];
    [_videoCamera.inputCamera unlockForConfiguration];
}

#pragma mark - 调节焦距
- (void)cameraBackgroundDidChangeFocus:(CGFloat)focus {
    if (![_videoCamera.inputCamera isFocusModeSupported:AVCaptureFocusModeContinuousAutoFocus]) return;
    if (![_videoCamera.inputCamera lockForConfiguration:nil]) return;
    [_videoCamera.inputCamera setFocusModeLockedWithLensPosition:focus completionHandler:nil];
    [_videoCamera.inputCamera unlockForConfiguration];
}

#pragma mark - 调节取景大小
- (void)cameraBackgroundDidChangeZoom:(CGFloat)zoom {
    if (![_videoCamera.inputCamera lockForConfiguration:nil]) return;
    [_videoCamera.inputCamera rampToVideoZoomFactor:zoom withRate:50];
    [_videoCamera.inputCamera unlockForConfiguration];
}

#pragma mark - 点击屏幕对焦
- (void)cameraBackgroundDidTap:(CGPoint)point {
    CGPoint location = point;
    CGPoint pointOfInerest = CGPointMake(0.5, 0.5);
    CGSize frameSize = _filteredVideoView.frame.size;
    if ([_videoCamera cameraPosition] == AVCaptureDevicePositionFront) location.x = frameSize.width - location.x;
    pointOfInerest = CGPointMake(location.y / frameSize.height, 1.f - (location.x / frameSize.width));
    if ([_videoCamera.inputCamera isFocusPointOfInterestSupported] && [_videoCamera.inputCamera isFocusModeSupported:AVCaptureFocusModeAutoFocus]) {
        if ([_videoCamera.inputCamera lockForConfiguration:nil]) {
            [_videoCamera.inputCamera setFocusPointOfInterest:pointOfInerest];
            [_videoCamera.inputCamera setFocusMode:AVCaptureFocusModeAutoFocus];
            if([_videoCamera.inputCamera isExposurePointOfInterestSupported] && [_videoCamera.inputCamera isExposureModeSupported:AVCaptureExposureModeContinuousAutoExposure]) {
                [_videoCamera.inputCamera setExposurePointOfInterest:pointOfInerest];
                [_videoCamera.inputCamera setExposureMode:AVCaptureExposureModeContinuousAutoExposure];
            }
            [_videoCamera.inputCamera unlockForConfiguration];
        }
    }
}

#pragma mark - 录制
- (void)cameraBackgroundDidClickPlay {
    NSString *pathToMovie =  [kCachePath stringByAppendingPathComponent:[self movieName]];
    unlink([pathToMovie UTF8String]);
    NSURL *movieURL = [NSURL fileURLWithPath:pathToMovie];
    _movieWriter = [[GPUImageMovieWriter alloc] initWithMovieURL:movieURL size:CGSizeMake(720, 1280)];
    _movieWriter.encodingLiveVideo = YES;
    [_saturationFilter addTarget:_movieWriter];
    _videoCamera.audioEncodingTarget = _movieWriter;
    [_movieWriter startRecording];
    _currentMoviePath = pathToMovie;
}

#pragma mark - 暂停
- (void)cameraBackgroundDidClickPause {
    [_saturationFilter removeTarget:_movieWriter];
    _videoCamera.audioEncodingTarget = nil;
    [_movieWriter finishRecording];
    NSInteger time = (int)(ceil(_movieWriter.duration.value / _movieWriter.duration.timescale));
    NSData *data = [NSData dataWithContentsOfFile:_currentMoviePath];
    
    NSMutableDictionary *moviePath = [NSMutableDictionary dictionary];
    moviePath[kMoviePath] = _currentMoviePath;
    moviePath[kMovieTime] = [NSString stringWithFormat:@"%zd", time];
    [self.moviePaths addObject:moviePath];
    NSLog(@"%zd--%zd", data.length, time);
}

#pragma mark - 保存
- (void)cameraBackgroundDidClickSave {
    NSMutableArray *paths = [NSMutableArray array];
    [self.moviePaths enumerateObjectsUsingBlock:^(NSDictionary * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        [paths addObject:obj[kMoviePath]];
    }];
    
    [self mergeVideosWithPaths:paths completed:^(NSString *videoPath) {
        ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
        if (![library videoAtPathIsCompatibleWithSavedPhotosAlbum:[NSURL URLWithString:videoPath]]) return;
        [library writeVideoAtPathToSavedPhotosAlbum:[NSURL URLWithString:videoPath] completionBlock:^(NSURL *assetURL, NSError *error) {
            if (error) {
                UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:@"Video Saving Failed"
                                                               delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
                [alert show];
                [self clearCache];
            } else {
                UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Video Saved" message:@"Saved To Photo Album"
                                                               delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil];
                [alert show];
                [self clearCache];
            }
        }];
    }];
}

#pragma mark - 删除
- (void)cameraBackgroundDidClickDeleteCompleted:(void (^)())completed {
    if (!self.moviePaths.count) {
        completed();
        return;
    };
    [self getCacheCompleted:^(NSString *string) {
        NSLog(@"删除前%@", string);
        NSFileManager *manager = [NSFileManager defaultManager];
        NSString *cachePath = self.moviePaths.lastObject[kMoviePath];
        [manager removeItemAtPath:cachePath error:nil];
        [self.moviePaths removeLastObject];
        completed();
        [self getCacheCompleted:^(NSString *string) {
            NSLog(@"删除后%@", string);
        }];
    }];
}

#pragma mark - 将分段视频合称为一个视频
- (void)mergeVideosWithPaths:(NSArray *)paths completed:(void(^)(NSString *videoPath))completed {
    if (!paths.count) return;
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        AVMutableComposition *mixComposition = [[AVMutableComposition alloc] init];
        AVMutableCompositionTrack *audioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
        AVMutableCompositionTrack *videoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
        CMTime totalDuration = kCMTimeZero;
        for (int i = 0; i < paths.count; i++) {
            AVURLAsset *asset = [AVURLAsset assetWithURL:[NSURL fileURLWithPath:paths[i]]];
            NSError *erroraudio = nil;
            AVAssetTrack *assetAudioTrack = [[asset tracksWithMediaType:AVMediaTypeAudio] firstObject];
            BOOL ba = [audioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration) ofTrack:assetAudioTrack atTime:totalDuration error:&erroraudio];
            NSLog(@"erroraudio:%@--%d", erroraudio, ba);
            NSError *errorVideo = nil;
            AVAssetTrack *assetVideoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo]firstObject];
            BOOL bl = [videoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration)
                                          ofTrack:assetVideoTrack
                                           atTime:totalDuration
                                            error:&errorVideo];
            NSLog(@"errorVideo:%@%d",errorVideo,bl);
            totalDuration = CMTimeAdd(totalDuration, asset.duration);
        }
        NSString *outPath = [kVideoPath stringByAppendingPathComponent:[self movieName]];
        NSURL *mergeFileURL = [NSURL fileURLWithPath:outPath];
        AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:mixComposition
                                                                          presetName:AVAssetExportPreset1280x720];
        exporter.outputURL = mergeFileURL;
        exporter.outputFileType = AVFileTypeMPEG4;
        exporter.shouldOptimizeForNetworkUse = YES;
        [exporter exportAsynchronouslyWithCompletionHandler:^{
            NSLog(@"exporter%@",exporter.error);
            if (exporter.error) return;
            dispatch_async(dispatch_get_main_queue(), ^{
                completed(outPath);
            });
        }];
    });
}

#pragma mark - 返回录制时间
- (NSArray *)cameraBackgroundMovies {
    return self.moviePaths;
}

#pragma mark - 内部处理方法
- (NSString *)movieName {
    NSDate *datenow = [NSDate date];
    NSString *timeSp = [NSString stringWithFormat:@"%ld", (long)[datenow timeIntervalSince1970]];
    return [timeSp stringByAppendingString:@".m4v"];
}

#pragma mark - 清除临时文件
- (void)clearCache {
    NSFileManager *manager = [NSFileManager defaultManager];
    NSString *cachePath = kCachePath;
    [manager removeItemAtPath:cachePath error:nil];
    
    BOOL isDirectory = NO;
    if ([manager fileExistsAtPath:cachePath isDirectory:&isDirectory]) {
        NSDirectoryEnumerator *enumerator = [manager enumeratorAtPath:cachePath];
        for (NSString *subPath in enumerator) {
            NSString *fullPath = [cachePath stringByAppendingPathComponent:subPath];
            NSDictionary *attrs = [manager attributesOfItemAtPath:fullPath error:nil];
            
            if ([attrs[NSFileType] isEqualToString:NSFileTypeDirectory]) continue;
            [manager removeItemAtPath:fullPath error:nil];
        }
    }
    [self getCacheCompleted:^(NSString *string) {
        NSLog(@"after delete: %@", string);
    }];
}

#pragma mark - 缓存大小
- (void)getCacheCompleted:(void(^)(NSString *string))completed {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSString *creatPath = kCachePath;
        NSString *fileSize=creatPath.fileSizeString;
        CGFloat size = 0.0;
        if ([fileSize hasSuffix:@"KB"]) {
            size += [fileSize doubleValue] * 0.001;
        } else if ([fileSize hasSuffix:@"MB"]) {
            size += [fileSize doubleValue];
        }
        dispatch_async(dispatch_get_main_queue(), ^{
            completed([NSString stringWithFormat:@"%.1lfM", size]);
        });
    });
}

@end
